﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;

#if UNITY_EDITOR
namespace Utility.Inspector
{
	public enum FlexibleTypes
	{
		None,
		Begin,
		End,
		Both,
	}

	public static class FlexibleTypesUtility
	{
		public static bool FlexibleBegin (this FlexibleTypes flexible)
		{
			return flexible == FlexibleTypes.Begin || flexible == FlexibleTypes.Both;
		}

		public static bool FlexibleEnd (this FlexibleTypes flexible)
		{
			return flexible == FlexibleTypes.End || flexible == FlexibleTypes.Both;
		}
	}

	public struct HorizontalScope : IDisposable
	{
		FlexibleTypes flexible;
		public Rect Rect { get; private set; }

		public static HorizontalScope New (FlexibleTypes flexible, GUIStyle style, params GUILayoutOption[] options)
		{
			var rect = EditorGUILayout.BeginHorizontal (style, options);
			if (flexible.FlexibleBegin ())
			{
				GUILayout.FlexibleSpace ();
			}

			return new HorizontalScope () { flexible = flexible, Rect = rect };
		}

		public static HorizontalScope New (FlexibleTypes flexible, params GUILayoutOption[] options)
		{
			return New (flexible, GUIStyle.none, options);
		}

		public static HorizontalScope New (GUIStyle style, params GUILayoutOption[] options)
		{
			return New (FlexibleTypes.None, style, options);
		}

		public static HorizontalScope New (params GUILayoutOption[] options)
		{
			return New (FlexibleTypes.None, GUIStyle.none, options);
		}

		public void Dispose ()
		{
			if (flexible.FlexibleEnd ())
			{
				GUILayout.FlexibleSpace ();
			}
			EditorGUILayout.EndHorizontal ();
		}
	}

	public struct VerticalScope : IDisposable
	{
		public Rect Rect { get; private set; }
		FlexibleTypes flexible;

		public static VerticalScope New (FlexibleTypes flexible, GUIStyle style, params GUILayoutOption[] options)
		{
			var rect = EditorGUILayout.BeginVertical (style, options);
			if (flexible.FlexibleBegin ())
			{
				GUILayout.FlexibleSpace ();
			}
			return new VerticalScope () { flexible = flexible, Rect = rect };
		}

		public static VerticalScope New (FlexibleTypes flexible, params GUILayoutOption[] options)
		{
			return New (flexible, GUIStyle.none, options);
		}

		public static VerticalScope New (GUIStyle style, params GUILayoutOption[] options)
		{
			return New (FlexibleTypes.None, GUIStyle.none, options);
		}

		public static VerticalScope New (params GUILayoutOption[] options)
		{
			return New (FlexibleTypes.None, GUIStyle.none, options);
		}

		public void Dispose ()
		{
			if (flexible.FlexibleEnd ())
			{
				GUILayout.FlexibleSpace ();
			}
			EditorGUILayout.EndVertical ();
		}
	}

	public struct GUIEnableScope : IDisposable
	{
		bool beginState;

		public static GUIEnableScope New (bool enable)
		{
			bool s = GUI.enabled;
			GUI.enabled = s ? enable : false;
			return new GUIEnableScope ()
			{
				beginState = s,
			};
		}

		public void Dispose ()
		{
			GUI.enabled = beginState;
		}
	}

	public struct GUIColorScope : IDisposable
	{
		Color recordColor;

		public static GUIColorScope New (bool condition, Color color)
		{
			var c = GUI.color;
			if (condition)
			{
				GUI.color = color;
			}
			return new GUIColorScope ()
			{
				recordColor = c,
			};
		}

		public static GUIColorScope New (Color color)
		{
			return New (true, color);
		}

		public void Dispose ()
		{
			GUI.color = recordColor;
		}
	}

	public struct ScrollViewScope : IDisposable
	{
		public Vector2 ScrollPos { get; private set; }

		public static ScrollViewScope New (Vector2 scrollPos, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options)
		{
			var group = new ScrollViewScope ();
			group.ScrollPos = EditorGUILayout.BeginScrollView (scrollPos, alwaysShowHorizontal, alwaysShowVertical, options);
			return group;
		}

		public static ScrollViewScope New (Vector2 scrollPos, params GUILayoutOption[] options)
		{
			return New (scrollPos, false, false, options);
		}

		public static ScrollViewScope New (ref Vector2 scrollPos, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options)
		{
			var group = new ScrollViewScope ();
			scrollPos = EditorGUILayout.BeginScrollView (scrollPos, alwaysShowHorizontal, alwaysShowVertical, options);
			group.ScrollPos = scrollPos;
			return group;
		}

		public static ScrollViewScope New (ref Vector2 scrollPos, params GUILayoutOption[] options)
		{
			return New (ref scrollPos, false, false, options);
		}

		public void Dispose ()
		{
			EditorGUILayout.EndScrollView ();
		}
	}

	public struct EditorGUIWidthScope : IDisposable
	{
		float storedLabelWidth;
		float storedFieldWidth;

		public static EditorGUIWidthScope New (float labelWidth, float fieldWidth)
		{
			EditorGUIWidthScope scope = new EditorGUIWidthScope ();
			scope.storedLabelWidth = EditorGUIUtility.labelWidth;
			scope.storedFieldWidth = EditorGUIUtility.fieldWidth;

			EditorGUIUtility.labelWidth = labelWidth;
			EditorGUIUtility.fieldWidth = fieldWidth;
			return scope;
		}

		public static EditorGUIWidthScope NewLabelWidth (float labelWidth)
		{
			return New (labelWidth, EditorGUIUtility.fieldWidth);
		}

		public static EditorGUIWidthScope NewFieldWidth (float fieldWidth)
		{
			return New (EditorGUIUtility.labelWidth, fieldWidth);
		}

		public void Dispose ()
		{
			EditorGUIUtility.labelWidth = storedLabelWidth;
			EditorGUIUtility.fieldWidth = storedFieldWidth;
		}
	}

	public struct EditorGUIChangeCheckScope : IDisposable
	{
		Action action;

		public static EditorGUIChangeCheckScope New (Action action)
		{
			var scope = new EditorGUIChangeCheckScope ()
			{
				action = action
			};

			EditorGUI.BeginChangeCheck ();
			return scope;
		}

		public void Dispose ()
		{
			if (EditorGUI.EndChangeCheck ())
			{
				action ();
			}
		}
	}

	public struct IndentLevelScope : IDisposable
	{
		int level;

		public static IndentLevelScope New (int level = 1)
		{
			IndentLevelScope scope = new IndentLevelScope ();
			scope.level = EditorGUI.indentLevel;

			EditorGUI.indentLevel += level;
			return scope;
		}

		public static IndentLevelScope Zero ()
		{
			IndentLevelScope scope = new IndentLevelScope ();
			scope.level = EditorGUI.indentLevel;

			EditorGUI.indentLevel = 0;
			return scope;
		}

		public void Dispose ()
		{
			EditorGUI.indentLevel = level;
		}
	}

	public struct GUIGroupScope : IDisposable
	{
		public static GUIGroupScope New (Rect position)
		{
			var scope = new GUIGroupScope ();

			GUI.BeginGroup (position);
			return scope;
		}


		public void Dispose ()
		{
			GUI.EndGroup ();
		}
	}
}
#endif